from datetime import datetime as DateTime
from typing import Union

from django.db import transaction

from backend.cews.models import (Parts, PartsImprovementWork, RepairOrder, RepairOrderInspection,StockItemRelation,
                                 RepairOrderPart, RepairOrderPartVersion,
                                 RepairOrderTask, StockItem, RepairOrderImprovementWork, StockItemImprovementWork)
from backend.cews.services.TaskService import create_task_for_repair_order_part
from backend.cews.services.utils import all_related_stock_code_by_stock_code, all_related_stock_code_by_stock_item
from backend.cews.models import Log
import json

def get_repair_order_imp_work(repair_order:Union[int, RepairOrder]):
    """
        Get all e-docs of a repair order

        Args:
            repair_order: RepairOrder instance or RepairOrder ID

        Returns:
            repair_order_imp_works: RepairOrderImprovementWork QuerySet
    """
    if isinstance(repair_order, int):
        repair_order = RepairOrder.objects.get(id=repair_order)

    return RepairOrderImprovementWork.objects.filter(repair_order=repair_order)

def get_stock_item_imp_work_by_stock_code(stock_code:str):
    '''
        Get list of stock item e-doc of a stock code
    '''
    sies = get_all_stock_item_imp_work_by_stock_code(stock_code)

    return sies

def get_all_stock_item_imp_work_by_stock_code(stock_code:str):
    '''
        get all stock item imp work of a stock code and its children
    '''
    stock_codes = all_related_stock_code_by_stock_code(stock_code)
    sie = StockItemImprovementWork.objects.filter(stock_code__in=stock_codes,invalid=0).select_related('stock_item')
    return sie

def create_stock_item_imp_work(
    stock_item: Union[int, StockItem],
    doc_name:str,
    description:str,
    completed:int=0,
    invalid:int=0,
):
    if isinstance(stock_item, int):
        stock_item = StockItem.objects.get(id=stock_item)
    
    sie = StockItemImprovementWork(
        stock_item=stock_item,
        stock_code=stock_item.stock_code,
        doc_name=doc_name,
        description=description,
        completed=completed,
        invalid=invalid,
    )
    sie.save()
    
    return sie


def create_repair_order_imp_works(repair_order:Union[int,RepairOrder]):
    
    if isinstance(repair_order, int):
        repair_order = RepairOrder.objects.get(id=repair_order, deleted=False) 
        
    primary_part = repair_order.primary_part
    
    sie = get_all_stock_item_imp_work_by_stock_code(primary_part.stock_code)
    
    roes = []
    
    ## check last repairorder edoc
    ## find last repair order
    primary_rop = repair_order.primary_part
    _part = primary_rop.part
    last_repair_order = _part.last_order

    # lroe_dict = {}
    # if last_repair_order:
    #     lroe_dict = {roe.stock_code+roe.doc_name:roe for roe in get_repair_order_imp_work(last_repair_order)}

    if primary_rop.is_parent:
        sie = StockItemImprovementWork.objects.filter(stock_code=primary_rop.stock_code, deleted=False)
        for s in sie:
            roes.append(create_repair_order_imp_work(repair_order,s,None,primary_rop.serial_no))

        for child in primary_rop.children:
            child_sies = StockItemImprovementWork.objects.filter(stock_code=child.stock_code, deleted=False)
            for child_sie in child_sies:
                roes.append(create_repair_order_imp_work(repair_order,child_sie,None,child.serial_no)) 

    # else:
    #     for s in sie:
    #         # last_repair_order_imp_work = lroe_dict.get(s.stock_code+s.doc_name,None)
    #         ## Default create all edoc
    #         roes.append(create_repair_order_imp_work(repair_order,s,None)) 

    return roes
        

def delete_stock_item_imp_work(id, request):
    sie = StockItemImprovementWork.objects.get(id=id)
    sie.delete()

    new_log = Log(
        username=request.user.username,
        log_type=Log.LogType.STOCKMASTER,
        category_type=Log.CategoryType.IMPWORK,
        action_type=Log.ActionType.DELETE,
        action='delete_stock_item_imp_work',
        payload= json.dumps(request.data),
        description=f"Stock Code:{sie.stock_code}, improvement work: {sie.doc_name} is deleted",
        repair_order=0,
        parent_stock_code='',
        parent_part='',
        part='',
        stock_code=sie.stock_code
        )
    new_log.save()
    
    return sie


def create_repair_order_imp_work(repair_order,edoc:StockItemImprovementWork,last_parts_imp_work:PartsImprovementWork=None,serial_no=''):
    """
        Create a repair order e-doc, pass last repair order e-doc to use last result
    """
    roe = RepairOrderImprovementWork(repair_order=repair_order)
    roe.stock_code = edoc.stock_code
    roe.serial_no = serial_no
    roe.doc_name = edoc.doc_name
    roe.description = edoc.description
    roe.version = edoc.version
    ## check latest version
    if last_parts_imp_work is not None and last_parts_imp_work.version == edoc.version:
        ## same version can use last result
        roe.complete = last_parts_imp_work.complete
    else:
        roe.complete = 0

    if edoc.file:
        roe.file_url = edoc.file.url

    roe.save()
    return roe

def toggle_complete_repair_order_imp_work(repair_order_imp_work:Union[int,RepairOrderImprovementWork]):
    if isinstance(repair_order_imp_work, int):
        repair_order_imp_work = RepairOrderImprovementWork.objects.get(id=repair_order_imp_work)
        parts_imp_work = PartsImprovementWork.objects.filter(parts=repair_order_imp_work.parts, doc_name=repair_order_imp_work.doc_name, version=repair_order_imp_work.version)
    
    # if repair_order_imp_work.complete >= 1:
    #     raise ValueError('Repair order e-doc is already completed')
    # else:

    
    if parts_imp_work.exists():
        if parts_imp_work.first().complete >= 1:
            raise ValueError('Parts improvement work is already completed')

    repair_order_imp_work.complete = 0 if repair_order_imp_work.complete > 0 else 1
    
    repair_order_imp_work.save()
    return repair_order_imp_work


def query_real_time_repair_order_imp_work(repair_order:Union[RepairOrder,int,str]):
    """
        query repair order imp work and check with parts imp work, if parts imp work is updated, update repair order imp work
    
        Args:
            repair_order: id or instnace
    
        Returns:
            repair order imp works
    """
    if isinstance(repair_order, int) or isinstance(repair_order, str):
        repair_order = RepairOrder.objects.get(id=repair_order)
        
    repair_order_imp_works = get_repair_order_imp_work(repair_order)
    parts = repair_order.get_parts_masters()
    parts_stock_code_dict = {p.stock_code+p.serial_no:p for p in parts}
    parts_imp_works = PartsImprovementWork.objects.filter(parts__in=parts)

    parts_imp_work_dict = {pe.stock_code+pe.doc_name+pe.version+pe.serial_no:pe for pe in parts_imp_works if pe.complete == 1}
    print(parts_imp_work_dict)
    to_update = [] ## list of repair order imp work to update
    res = [] ## list of repair order imp work to return
    
    for roe in repair_order_imp_works:
        key = roe.stock_code+roe.doc_name+roe.version+roe.serial_no
        parts_imp_work = parts_imp_work_dict.get(key,None)
        updated = False
        if parts_imp_work is not None:
            print(f"found parts imp work {parts_imp_work}- {parts_imp_work.stock_code} {parts_imp_work.serial_no} {parts_imp_work.doc_name}")
            if roe.complete != 1:
                roe.complete = 1
                roe.serial_no = parts_imp_work.parts.serial_no
                roe.parts = parts_imp_work.parts
                updated = True
        else:
            print(f"not found part imp work {roe.serial_no}")

        parts = parts_stock_code_dict.get(roe.stock_code+roe.serial_no,None)
        print(parts)
        if parts:
            if roe.parts != parts:
                roe.parts = parts
                roe.serial_no = parts.serial_no
                updated = True
        
        ## do not update when the order is already completed
        if repair_order.status == 'COMPLETE' or  repair_order.status == 'COMPLETE*':
            updated = False
            
        if updated:
            to_update.append(roe)
            
        res.append(roe)
        
    RepairOrderImprovementWork.objects.bulk_update(to_update,['complete','serial_no','parts'])
    # raise ValueError('TEST')
    return res
    
